package cn.bigfire.wedesk;

import com.google.googlejavaformat.java.Formatter;
import com.google.protobuf.Internal;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 工具类  根据Proto 生成Builder格式的java类，生成 java bean对象 (JavaBean对象方便JSON操作)
 */
public class GenBean {

    public static String currDir = System.getProperty("user.dir");
    public static String projectDir = currDir + "/wedesk-common/src/main/java/";

    public static void main(String[] args) {
//        genBean(projectDir,Im.class, "com.protobuf.bean");
//        genBean(projectDir,TWO.class, "com.protobuf.bean.demo.two");
        genBean(projectDir, "cn.bigfire.wedesk.pb", "cn.bigfire.wedesk.bean");
    }


    /**
     * 根据pb文件夹，生成bean类
     *
     * @param packageName pb包
     * @param projectDir 项目路径
     * @param beanPackageName bean包
     */
    public static void genBean(String projectDir,String packageName, String beanPackageName) {
        try {
            String pbPath = projectDir + packageName.replace(".", "/");
            String beanPath = projectDir + beanPackageName.replace(".", "/");
            File[] files = new File(pbPath).listFiles();
            if (files == null) {
                return;
            }
            for (File file : files) {
                if (file.isDirectory()) {//文件夹
                    genBean( projectDir,packageName + "." + file.getName(), beanPackageName + "." + file.getName());//进一层
                } else if (file.isFile() && file.getName().endsWith(".java") && !file.getName().endsWith("OrBuilder.java")) {//java文件
                    String className = file.getName().replace(".java", "");
                    Class<?> aClass = Class.forName(packageName + "." + className);
                    genBean(projectDir,aClass, beanPackageName);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 根据 Protobuf 的java对象 生成对应的 JavaBean对象
     *
     * @param clazz 类
     * @param projectDir 项目路径
     * @param beanPackageName bean的存放包位置
     */
    public static void genBean(String projectDir,Class<?> clazz, String beanPackageName) {
        try {
            String dir = projectDir + "/" + beanPackageName.replace(".", "/");
            String fileName = dir + "/" + clazz.getSimpleName() + ".java";
            String classStr = classStr(clazz);
            classStr = classStr.replace("$", ".");
            classStr = classStr.replace("final", "");
//            classStr = classStr.replace(clazz.getPackage().getName()+".", beanPackageName+".");
            classStr = classStr.replace(clazz.getPackage().getName()+".", "");

            StringBuilder importSb = new StringBuilder();

            if (classStr.contains("java.util.")) {
                importSb.append("import java.util.*;\n");
                classStr = classStr.replace("java.util.", "");
            }
            if (classStr.contains("com.google.protobuf.ByteString")) {
                classStr = classStr.replace("com.google.protobuf.ByteString","java.lang.String");
            }
            if (classStr.contains("java.lang.String")) {
                importSb.append("import java.lang.String;\n");
                classStr = classStr.replace("java.lang.String", "String");
            }
            classStr = String.format("package %s;\n\n", beanPackageName) + importSb.toString() + classStr;
            String formattedClassStr = new Formatter().formatSource(classStr);
            write(fileName, formattedClassStr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void write(String filePath, String content) {
        System.out.println(filePath);
        Path path = Paths.get(filePath);
        try {
            // 如果目录不存在，则创建目录
            Files.createDirectories(path.getParent());
            // 将字符串写入文件，如果文件已存在，则覆盖
            Files.write(path, content.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //字段 字符串
    public static String fieldStr(String fieldTypeName, String fieldName) {
        if (fieldTypeName.contains("java.util.List")) {
            fieldName += " = new java.util.ArrayList<>()";
        }
        if (fieldTypeName.contains("java.util.Map")) {
            fieldName += " = new java.util.HashMap<>()";
        }
        return String.format("%s %s;\n", fieldTypeName, fieldName);
    }

    //get set方法 字符串
    public static String getSetTlp = "    public %s get%s() {\n" +
            "        return %s;\n" +
            "    }\n" +
            "    public void set%s(%s %s) {\n" +
            "        this.%s = %s;\n" +
            "    }\n";
    public static String getSetStr(String fieldTypeName, String fieldName) {
        String upFieldName = firstUpper(fieldName);
        return String.format(getSetTlp, fieldTypeName, upFieldName, fieldName, upFieldName, fieldTypeName, fieldName, fieldName, fieldName);
    }

    //class 字符串
    public static String classTlp = "%s {\n" +
            "%s " +
            "}\n";
    public static String classStr(Class<?> clazz) {
        Field[] declaredFields = clazz.getDeclaredFields();
        StringBuilder fieldSb = new StringBuilder();
        StringBuilder getSetSb = new StringBuilder();
        for (Field field : declaredFields) {
            if (field.getName().endsWith("_") && !field.getName().equals("bitField0_")) {
                String fieldTypeName = field.getType().getName();
                String fieldName_ = field.getName();
                String fieldName = fieldName_.substring(0, fieldName_.length() - 1);

                //String
                if (fieldTypeName.endsWith("StringList")) {
                    fieldTypeName = "java.util.List<String>";
                }
                //String
                if (Object.class.equals(field.getType())) {
//                    System.out.println("String:" + field.getName());
                    String firstUpper = firstUpper(fieldName);
                    try {
                        Method method = clazz.getDeclaredMethod("get" + firstUpper);
                        Class<?> returnType = method.getReturnType();
                        fieldTypeName = returnType.getName();
                    } catch (NoSuchMethodException e) {
                        continue;
                    }
                }
                //list
                if (List.class.equals(field.getType())) {
//                    System.out.println("list:" + field.getName());
                    Type genericType = field.getGenericType();
                    if (genericType instanceof ParameterizedType) {// 如果是泛型参数的类型
                        ParameterizedType pt = (ParameterizedType) genericType;
                        Type actualTypeArgument = pt.getActualTypeArguments()[0];//得到泛型里的class类型对象
                        String typeName = actualTypeArgument.getTypeName().replace("$", ".");
                        fieldTypeName += "<" + typeName + ">";
                    }
                }
                //map
                if (fieldTypeName.startsWith("com.google.protobuf.MapField")) {
//                    System.out.println("map:" + field.getName());
                    fieldTypeName = fieldTypeName.replace("com.google.protobuf.MapField", "java.util.Map");
                    Type genericType = field.getGenericType();
                    if (genericType instanceof ParameterizedType) {// 如果是泛型参数的类型
                        ParameterizedType pt = (ParameterizedType) genericType;
                        Type[] args = pt.getActualTypeArguments();
                        if (args.length == 2) {
                            fieldTypeName += String.format("<%s,%s>", args[0].getTypeName().replace("$", "."), args[1].getTypeName().replace("$", "."));
                        }
                    }
                }
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.Internal$IntList", "java.util.List<Integer>");
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.Internal$LongList", "java.util.List<Long>");
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.Internal$FloatList", "java.util.List<Float>");
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.Internal$DoubleList", "java.util.List<Double>");
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.Internal$BooleanList", "java.util.List<Boolean>");
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.Internal$ByteString", "String");
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.LazyStringArrayList", "java.util.List<String>");
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.Internal$ProtobufList", "java.util.List<String>");
                fieldTypeName = fieldTypeName.replace("com.google.protobuf.LazyStringArrayList", "java.util.List<String>");

                fieldSb.append("\t").append(fieldStr(fieldTypeName, isKeyword(fieldName) ? fieldName_ : fieldName));
                getSetSb.append(getSetStr(fieldTypeName, isKeyword(fieldName) ? fieldName_ : fieldName));
            }

        }

        StringBuilder classSb = new StringBuilder();
        Class<?>[] declaredClasses = clazz.getDeclaredClasses();
        for (Class<?> innerClass : declaredClasses) {
            if (innerClass.getSimpleName().endsWith("Builder") || innerClass.getSimpleName().endsWith("EntryHolder")) {
                continue;
            }
            if (innerClass.isEnum()) {
                String enumStr = enumStr((Class<Internal.EnumLite>) innerClass);
                classSb.append("\t").append(enumStr);
            } else {
                classSb.append("\t").append(classStr(innerClass));
            }
        }
        String content = fieldSb.toString() + getSetSb + classSb;
        String classGenericName = clazz.toGenericString().replace("$", ".").replace(clazz.getCanonicalName(), "") + clazz.getSimpleName();
        return String.format(classTlp, classGenericName, content);
    }

    public static String constructorTlp = "private %s(int value) {\n" +
            "            this.value = value;\n" +
            "        }\npublic int getValue() {\n" +
            "            return value;\n" +
            "        }";
    public static String enumStr(Class<Internal.EnumLite> clazz) {
        Internal.EnumLite[] enumConstants = clazz.getEnumConstants();
        StringBuilder enumSb = new StringBuilder();
        if (enumConstants != null) {
            for (Internal.EnumLite enumConstant : enumConstants) {
                if (!"UNRECOGNIZED".equals(enumConstant.toString())) {
                    enumSb.append("\t\t").append(enumConstant).append("(").append(enumConstant.getNumber()).append("),\n");
                }
            }
        }

        String content = enumSb + ";\nprivate final int value;\n" + String.format(constructorTlp,clazz.getSimpleName());
        String classGenericName = clazz.toGenericString().replace("final", "").replace("$", ".").replace(clazz.getCanonicalName(), "") + clazz.getSimpleName();
        return String.format(classTlp, classGenericName, content);
    }


//    static ArrayList<Class<?>> protoClassList = new ArrayList<>();
//    static ArrayList<Class<?>> protoTypeList = new ArrayList<>();

    private static final Set<String> keywords = new HashSet<>();
    public static boolean isKeyword(String word) {
        return keywords.contains(word);
    }
    static {
        //Java 中的50个关键字
        Collections.addAll(keywords,
                "abstract", "assert", "boolean", "break", "byte",
                "case", "catch", "char", "class", "const",
                "continue", "default", "do", "double", "else",
                "enum", "extends", "final", "finally", "float",
                "for", "goto", "if", "implements", "import",
                "instanceof", "int", "interface", "long", "native",
                "new", "package", "private", "protected", "public",
                "return", "short", "static", "strictfp", "super",
                "switch", "synchronized", "this", "throw", "throws",
                "transient", "try", "void", "volatile", "while");

//        protoClassList.add(GeneratedMessage.class);//protobuf2
//        protoClassList.add(com.google.protobuf.GeneratedMessageV3.class);//protobuf3
//        protoTypeList.add(Descriptors.Descriptor.class);
//        protoTypeList.add(GeneratedMessage.FieldAccessorTable.class);
////        protoTypeList.add(GeneratedMessageV3.FieldAccessorTable.class);
//        protoTypeList.add(Descriptors.FileDescriptor.class);
    }

    public static String firstUpper(String original) {
        if (original == null || original.isEmpty()) {
            return original; // 返回原始字符串以避免NullPointerException
        }
        return original.substring(0, 1).toUpperCase() + original.substring(1);
    }

}
